package gitee

import (
	"bytes"
	"errors"
	"gitee.com/oscstudio/gitee/eris"
	"math/bits"
	"strings"
)

// ByteCat return bytes.
func ByteCat(sv ...string) []byte {
	var buf bytes.Buffer
	var size int
	for _, s := range sv {
		size += len(s)
	}
	buf.Grow(size)
	for _, s := range sv {
		_, _ = buf.WriteString(s)
	}
	return buf.Bytes()
}

// StrCat cat strings:
// You should know that StrCat gradually builds advantages
// only when the number of parameters is> 2.
func StrCat(sv ...string) string {
	var sb strings.Builder
	var size int
	for _, s := range sv {
		size += len(s)
	}
	sb.Grow(size)
	for _, s := range sv {
		_, _ = sb.WriteString(s)
	}
	return sb.String()
}

// StrSplitSkipEmpty skip empty string suggestcap is suggest cap
func StrSplitSkipEmpty(s string, sep byte, suggestcap int) []string {
	sv := make([]string, 0, suggestcap)
	var first, i int
	for ; i < len(s); i++ {
		if s[i] != sep {
			continue
		}
		if first != i {
			sv = append(sv, s[first:i])
		}
		first = i + 1
	}
	if first < len(s) {
		sv = append(sv, s[first:])
	}
	return sv
}

// PathSplit split path and
func PathSplit(p string, suggestcap int) []string {
	pv := make([]string, 0, suggestcap)
	var first, i int
	for ; i < len(p); i++ {
		if p[i] != '/' {
			continue
		}
		if first != i {
			s := p[first:i]
			switch s {
			case ".":
			case "..":
				if len(pv) == 0 {
					return pv
				}
				pv = pv[0 : len(pv)-1]
			default:
				pv = append(pv, s)
			}
		}
		first = i + 1
	}
	if first < len(p) {
		s := p[first:i]
		switch s {
		case ".":
		case "..":
			if len(pv) == 0 {
				return pv
			}
			pv = pv[0 : len(pv)-1]
		default:
			pv = append(pv, s)
		}
	}
	return pv
}

//StrSplitSkipSpecNoEmpty todo
func StrSplitSkipSpecNoEmpty(s, spec string, sep byte, suggestcap int) []string {
	sv := StrSplitSkipEmpty(s, sep, suggestcap)
	sv2 := make([]string, 0, suggestcap)
	for _, s := range sv {
		if s != spec {
			sv2 = append(sv2, s)
		}
	}
	return sv2
}

// HasNonBmpRune todo
// BMP -> U+0000 to U+FFFF
// https://www.sttmedia.com/unicode-basiclingualplane
func HasNonBmpRune(s string) bool {
	// ==> for _, v := range []rune(s)
	// https://github.com/golang/go/blob/c2edcf4b1253fdebc13df8a25979904c3ef01c66/src/cmd/compile/internal/gc/range.go#L358
	for _, r := range s {
		if r > '\uFFFF' {
			return true
		}
	}
	return false
}

const hdigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

func escapeRune(sb *strings.Builder, r rune) {
	var a [32 + 1]byte
	i := len(a)
	shift := uint(bits.TrailingZeros(uint(16))) & 7
	b := uint64(16)
	m := uint(16) - 1 // == 1<<shift - 1
	u := uint64(r)
	for u >= b {
		i--
		a[i] = hdigits[uint(u)&m]
		u >>= shift
	}
	// u < base
	i--
	a[i] = hdigits[uint(u)]
	sl := len(a) - i
	_, _ = sb.WriteString("\\U")
	for i := 0; i < 8-sl; i++ {
		_ = sb.WriteByte('0')
	}
	_, _ = sb.Write(a[i:])
}

// EscapeNonBmpRune escape non BMP rune
func EscapeNonBmpRune(s string) string {
	var sb strings.Builder
	sb.Grow(len(s))
	for _, r := range s {
		if r == '\\' {
			_, _ = sb.Write([]byte(`\\`))
			continue
		}
		if r > '\uFFFF' {
			escapeRune(&sb, r)
			continue
		}
		_, _ = sb.WriteRune(r)
	}
	return sb.String()
}

// ErrorCat todo
func ErrorCat(sv ...string) error {
	return errors.New(StrCat(sv...))
}

// MakeError todo
func MakeError(sv ...string) error {
	return eris.New(StrCat(sv...))
}

// WarpError todo
func WarpError(err error, sv ...string) error {
	return eris.Wrap(err, StrCat(sv...))
}
